package com.gwl.concurrent.interval;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import static java.lang.System.out;

class Horse implements Runnable { 
	private static final AtomicInteger COUNTER = new AtomicInteger(0); 
	private static final int MAX_LENGTH_OF_STRIDE = 3; 
	private static final Random RANDOM = new Random(47); 

	private final int id = COUNTER.incrementAndGet(); 
	private int strides = 0; 
	private CyclicBarrier barrier; 
	
	public Horse(CyclicBarrier b) { 
		barrier = b; 
	}
	
	public synchronized int getStrides() { return strides; } 
	@Override
	public void run() {
		while(!Thread.interrupted()) { 
			synchronized(this) { 
				strides += RANDOM.nextInt(MAX_LENGTH_OF_STRIDE); 
			} 
			try {
				barrier.await(); 
			} catch (InterruptedException e) {
				// A legitimate way to exit
			} catch (BrokenBarrierException e) {
				// This one we want know about
				throw new RuntimeException(); 
			} 
		}
	} 
	public String toString() { return "House " + id + " "; } 
	public String tracks() { 
		StringBuilder buff = new StringBuilder(); 
		for(int i = getStrides(); i > 0; i--) 
			buff.append("*"); 
		buff.append(id); 
		return buff.toString(); 
	}
	
}

public class HorseRace {

	private final int FINISH_LINE = 75; 
	private List<Horse> horses = new ArrayList<Horse>(); 
	private CyclicBarrier barrier; 
	private ExecutorService exec = Executors.newCachedThreadPool(); 
	public HorseRace(int nHorses, final int pause) { 
		barrier = new CyclicBarrier(nHorses, new Runnable(){
			@Override
			public void run() {
				StringBuilder buff = new StringBuilder(); 
				for(int i = 0; i < FINISH_LINE; i++) 
					buff.append("="); 
				out.println(buff.toString()); 
				for(Horse horse: horses) 
					out.println(horse.tracks()); 
				for(Horse horse: horses) { 
					if(horse.getStrides() >= FINISH_LINE) { 
						out.println(horse + "won!"); 
						exec.shutdownNow(); 
						return; 
					}
				} 
				try {
					TimeUnit.MILLISECONDS.sleep(pause);
				} catch (InterruptedException e) {
					out.println("barrier-action sleep interrupted"); 
				} 
			} 
		}); 
		for(int i = 0; i < nHorses; i++) { 
			Horse horse = new Horse(barrier); 
			horses.add(horse); 
			exec.execute(horse); 
		} 
	}
	public static void start(int nHorses, int pause) { 
		new HorseRace(nHorses, pause); 
	}
	
	public static final int DEFAULT_HORSE_NUM = 7; 
	public static final int DEFAULT_PAUSE = 200; 
	public static void main(String[] args) {
		int horseNum; 
		int pause; 
		if(args.length > 0) { 
			int n = Integer.valueOf(args[0]); 
			if(n <= 0) 
				throw new IllegalArgumentException("horseNum(" + n + ") must bigger than 0"); 
			horseNum = n; 
		} else { 
			horseNum = DEFAULT_HORSE_NUM; 
		} 
		if(args.length > 1) { 
			int p = Integer.valueOf(args[1]); 
			if(p < 0) 
				throw new IllegalArgumentException("pause milliseconds(" + p + ") must bigger than -1"); 
			pause = p; 
		} else { 
			pause = DEFAULT_PAUSE; 
		} 
		HorseRace.start(horseNum, pause); 
	} 

}
